module system(
  input clk,
  input rst,
  output uart_tx
);

wire        cpu_hready;
wire        cpu_hresp;
wire [31:0] cpu_hrdata;
wire        cpu_icache_data_valid;
wire [31:0] cpu_icache_data;
wire [ 1:0] cpu_htrans;
wire        cpu_hwrite;
/* verilator lint_off UNUSED */
wire [31:0] cpu_haddr;
/* verilator lint_on UNUSED */
wire [ 2:0] cpu_hsize;
wire [31:0] cpu_hwdata;
wire        cpu_icache_rd_o;
/* verilator lint_off UNUSED */
wire [31:2] cpu_icache_addr_o;
/* verilator lint_on UNUSED */

// Boot RAM
wire        s0_hsel;
wire        s0_hready;
wire        s0_hresp;
wire [31:0] s0_hrdata;

wire        ram_read_instr_i;
wire [31:0] ram_data_instr_o;
wire        ram_inst_rdy_o;

// Timer
wire        s1_hsel;
wire        s1_hready;
wire        s1_hresp;
wire [31:0] s1_hrdata;

wire        timer_irq_o;

// UART
wire        s2_hsel;
wire        s2_hready;
wire        s2_hresp;
wire [31:0] s2_hrdata;

reg s0_hsel_r;
reg s1_hsel_r;
reg s2_hsel_r;

always @(posedge clk) begin
  s0_hsel_r <= s0_hsel;
  s1_hsel_r <= s1_hsel;
  s2_hsel_r <= s2_hsel;
end

// memory map
assign s0_hsel = {cpu_haddr[31:16], 16'b0} == 32'h00000000;
assign s1_hsel = {cpu_haddr[31: 3],  3'b0} == 32'h00010000;
assign s2_hsel = {cpu_haddr[31: 2],  2'b0} == 32'h00010008;

assign cpu_hready =
  s0_hsel_r & s0_hready |
  s1_hsel_r & s1_hready |
  s2_hsel_r & s2_hready ;

assign cpu_hresp =
  s0_hsel_r & s0_hresp |
  s1_hsel_r & s1_hresp |
  s2_hsel_r & s2_hresp ;

assign cpu_hrdata =
  {32{s0_hsel_r}} & s0_hrdata |
  {32{s1_hsel_r}} & s1_hrdata |
  {32{s2_hsel_r}} & s2_hrdata ;

assign ram_read_instr_i = {cpu_icache_addr_o[31:16], 16'b0} == 32'h00000000 & cpu_icache_rd_o;
assign cpu_icache_data_valid = ram_inst_rdy_o;
assign cpu_icache_data = ram_data_instr_o;

ram #(16) ram(
  .clk(clk),
  .rst(rst),
  .htrans(cpu_htrans),
  .hsel(s0_hsel),
  .hwrite(cpu_hwrite),
  .haddr(cpu_haddr[15:0]),
  .hsize(cpu_hsize),
  .hwdata(cpu_hwdata),
  .hready(s0_hready),
  .hresp(s0_hresp),
  .hrdata(s0_hrdata),
  .inst_addr_valid(ram_read_instr_i),
  .inst_addr(cpu_icache_addr_o[15:2]),
  .inst_data_valid(ram_inst_rdy_o),
  .inst_data(ram_data_instr_o)
);

/*
timer timer(
  .clk(clk),
  .rst(rst),
  .htrans(cpu_htrans),
  .hsel(s1_hsel),
  .hwrite(cpu_hwrite),
  .haddr(cpu_haddr[2:0]),
  .hsize(cpu_hsize),
  .hwdata(cpu_hwdata),
  .hready(s1_hready),
  .hresp(s1_hresp),
  .hrdata(s1_hrdata),
  .o_irq(timer_irq_o)
);
*/

assign s1_hready = 1'b1;
assign s1_hresp = 1'b0;
assign s1_hrdata = 0;
assign timer_irq_o = 1'b0;

/*
uart #(1) uart(
  .clk(clk),
  .rst(rst),
  .htrans(cpu_htrans),
  .hsel(s2_hsel),
  .hwrite(cpu_hwrite),
  .haddr(cpu_haddr[15:0]),
  .hsize(cpu_hsize),
  .hwdata(cpu_hwdata),
  .hready(s2_hready),
  .hresp(s2_hresp),
  .hrdata(s2_hrdata),
  .o_txd(uart_tx)
);
*/

assign s2_hready = 1'b1;
assign s2_hresp = 1'b0;
assign s2_hrdata = 0;
assign uart_tx = 1'b1;

cpu cpu(
  .clk(clk),
  .rst(rst),
  .hready(cpu_hready),
  .hresp(cpu_hresp),
  .hrdata(cpu_hrdata),
  .i_icache_rdy(cpu_icache_data_valid),
  .i_icache_dat(cpu_icache_data),
  .i_irq(timer_irq_o),
  .htrans(cpu_htrans),
  .hwrite(cpu_hwrite),
  .haddr(cpu_haddr),
  .hsize(cpu_hsize),
  .hwdata(cpu_hwdata),
  .o_icache_rd(cpu_icache_rd_o),
  .o_icache_addr(cpu_icache_addr_o)
);

endmodule
